/*
 * $AIST_Release: 0.9.0 $
 * Copyright 2011 Information Technology Research Institute, National
 * Institute of Advanced Industrial Science and Technology
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *    http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package parser

import scala.util.parsing.input.Positional
import DataTypes._
import aggr.{SerializedTypes => ST}

/**
 * The module to define AST nodes.
 */
object Nodes {

  /**
   * Base trait.
   */
  sealed trait Node extends TreeWalker.Node

  /**
   * Variable information.
   */
  trait VarInfo {
    /**
     * Variable identifier.
     */
    val id: Int 

    /**
     * Variable name.
     */
    val name: String
  }

  // Expressions

  /**
   * Expression base trait.
   */
  sealed trait Expr extends Node with Positional {
    /**
     * Expression data type.
     */
    val dataType: DataType = UnkownType
  }

  /**
   * Binary operator.
   */
  case class BinOp(op: String, left: Expr, right: Expr, override val dataType: DataType) extends Expr

  object BinOp {
    def apply(op: String, left: Expr, right: Expr) = new BinOp(op, left, right, UnkownType)
  }

  /**
   * Unary operator.
   */
  case class UnOp(op: String, arg: Expr, override val dataType: DataType) extends Expr

  object UnOp {
    def apply(op: String, arg: Expr) = new UnOp(op, arg, UnkownType)
  }

  /**
   * Member access operator(.).
   */
  case class MemAccOp(tuple: Expr, member: String, override val dataType: DataType) extends Expr

  object MemAccOp {
    /**
     * Makes UnkownType member access operator.
     */
    def apply(t: Expr, m:String) = new MemAccOp(t, m, UnkownType)
  }

  /**
   * Assign operator.
   */
  case class AssignOp(lhs: Expr, rhs: Expr) extends Expr {
    override val dataType = IntType // Always return 0
  }

  /**
   * And(&amp;&amp;) operator
   */
  case class AndOp(lhs: Expr, rhs: Expr) extends Expr {
    override val dataType = IntType // Always return Int
  }

  /**
   * Or(||) operator
   */
  case class OrOp(lhs: Expr, rhs: Expr) extends Expr {
    override val dataType = IntType // Always return Int
  }

  /**
   * Container access operator([]).
   */
  case class ContainerAccessOp(container: Expr, index: Expr, override val dataType: DataType) extends Expr {
    override def toString() = "%s[%s]".format(container, index)
  }

  /**
   * Variable.
   */
  case class Variable(name: String) extends Expr {
    override def toString() =" Variable(%s)".format(name)
  }

  /**
   * Static variable.
   */
  case class StaticVariable(
    override val name: String, 
    override val dataType: DataType, 
    override val id :Int) extends Expr with VarInfo{
    override def toString() = {"StaticVariable(%s:%s)".format(name, dataType)}
  }

  /**
   * Local variable.
   */
  case class LocalVariable(
    override val name: String, 
    override val dataType: DataType, 
    override val id:Int) extends Expr with VarInfo{
    require(dataType.isInstanceOf[RefType])
    override def toString() = {"LocalVariable(%s:%s)".format(name, stripRef(dataType))}
  }

  /**
   * Global variable.
   */
  case class GlobalVariable(
    override val name: String, 
    override val dataType: DataType, 
    override val id:Int) extends Expr with VarInfo {
    override def toString() = {"GlobalVariable(%s:%s)".format(name, dataType)}
  }
  
  /**
   * Integer literal.
   */
  case class IntLit(value: Long) extends Expr {
    override val dataType = IntType
  }

  /**
   * String literal.
   */
  case class StringLit(value: String) extends Expr {
    override val dataType = StringType
    override def toString() = "\"%s\"".format(value)
  }

  /**
   * Float literal.
   */
  case class FloatLit(value: Double) extends Expr {
    override val dataType = FloatType
    override def toString() = value.toString
  }

  /**
   * "Undef" literal.
   */
  case class UndefLit(override val dataType: DataType) extends Expr {
    override def toString() = "Undef"
  }

  /**
   * Function call.
   */
  case class FuncCall(expr: Expr, args: List[Expr]) extends Expr {
    override val dataType = stripRef(expr.dataType) match {
      case f: FunctionType => f.retType
      case _               => UnkownType
    }
  }

  /**
   * Function literal.
   */
  case class FuncLit(ret: DataType, args: List[Label], stmts: List[Stmt]) extends Expr {
    override val dataType = FunctionType(ret, args.map {_.dataType})
  }

  /**
   * Analyzed function literal.
   */
  case class AnalyzedFuncLit(ret: DataType, args: List[Arg], stmts: List[Stmt]) extends Expr {
    override val dataType = FunctionType(ret, args.map {_.dataType})
  }

  /**
   * Function
   */
  abstract class Function(ftype: FunctionType) extends Expr {
    override val dataType = ftype
  }

  /**
   * Build-in function.
   */
  case class BuildInFunction(name: String, ftype: FunctionType) extends Function(ftype)

  /**
   * Type converter.
   */
  case class Converter(name: String, ftype: FunctionType) extends Function(ftype)

  /**
   * Map expression.
   */
  case class MapExpr(content: List[(Expr, Expr)], keyType: DataType, valueType: DataType) extends Expr {
    override val dataType = MapType(keyType, valueType)
  }

  /**
   * Array expression.
   */
  case class ArrayExpr(content: List[Expr], conType: DataType) extends Expr {
    override val dataType = ArrayType(conType)
  }

  /**
   * Tuple expression.
   */
  case class TupleExpr(content: List[Expr], override val dataType: TupleType) extends Expr

  /**
   * Enum expression.
   */
  case class EnumExpr(value: Int, d: DataType) extends Expr {
    override val dataType = d
  }

  /**
   * The expression to strip reference attribute.
   */
  case class ValueExpr(expr: Expr) extends Expr {
    override val dataType = expr.dataType match {
      case RefType(x) => x
      case x          => x
    }
  }

  /**
   * Strips reference attribute.
   */
  def stripRefExpr(expr: Expr) = expr.dataType match {
    case _:RefType => ValueExpr(expr).setPos(expr.pos)
    case _         => expr
  }

  // Initializer

  /**
   * Initializer base trait 
   */
  sealed trait Initializer extends Node with Positional 

  /**
   * List initializer
   *
   * {a, b, c,...}
   */
  case class ListInitializer(items: List[Initializer]) extends Initializer

  /**
   * Map initializer
   *
   * {k1:v1, k2:v2,...}
   */
  case class MapInitializer(items: List[(Initializer, Initializer)]) extends Initializer

  /**
   * Expression initializer
   */
  case class ExprInitializer(expr: Expr) extends Initializer

  // Statement

  /**
   * Statement base trait
   */
  sealed trait Stmt extends Node with Positional

  /**
   * Initializer in for-statement
   */
  sealed trait ForInitializer extends Stmt

  /**
   * Variable and type scope.
   */
  case class Scope(stmt: Stmt) extends Stmt

  /**
   * Expression statement
   */
  case class ExprStmt(expr:Expr) extends Stmt with ForInitializer

  /**
   * Definition of variables.
   */
  case class DefVarStmt(mod: Option[String], name: List[Identifier], dataType: DataType, init: Option[Initializer]) extends Stmt with ForInitializer

  /**
   * Definition of table.
   */
  case class DefTableStmt(name: List[Identifier], info: TableInfo) extends Stmt

  /**
   * Definition of table with table serialization information.
   */
  case class DefTableStmtWithSerializationInfo(name: List[Identifier], info: TableInfo, serializationInfo: TableSerializationInfo) extends Stmt

  /**
   * Table information.
   */
  case class TableInfo(
    /**
     * Aggregator type.
     */
    aggr: Identifier,
    /**
     * An argument
     */
    arg: Option[Expr], 
    /**
     * Key types.
     */
    indexes: List[Label], 
    /**
     * Contents type.
     */
    conType: Label, 
    /**
     * Options.
     */
    opts: List[TableOption]) extends Node

  /**
   * Information to serialize table.
   */
  case class TableSerializationInfo(
    /**
     * Key serialized type.
     */
    key: ST.SerializedType,
    /**
     * Arguments serialized type.
     */
    args: List[ST.SerializedType],
    /**
     * Content serialized type
     */
    value: ST.SerializedType)

  /**
   * Type definition statement 
   */
  case class DefType(name: String, impl: DataType) extends Stmt

  /**
   * Emit statement.
   */
  case class EmitStmt(table: Expr, keys: List[Expr], value: Expr, opts: List[EmitOption]) extends Stmt
  /**
   * Analyzed emit statement.
   */
  case class AnalyzedEmitStmt(table: Expr, key: Expr, value: Expr) extends Stmt

  /**
   * Proto statement.
   */
  case class ProtoStmt(file:String) extends Stmt

  /**
   * "if" statement.
   */
  case class IfStmt(cond: Expr, thenStmt:Stmt, elseStmt:Option[Stmt]) extends Stmt

  /**
   * "when" statement.
   */
  case class WhenStmt(vars: List[WhenDefVar], cond:Expr, stmt:Stmt) extends Stmt

  /**
   * "break" statement.
   */
  case class BreakStmt() extends Stmt

  /**
   * "continue" statement.
   */
  case class ContinueStmt() extends Stmt

  /**
   * Empty statement.
   */
  case class EmptyStmt() extends Stmt

  /**
   * "return" statement.
   */
  case class ReturnStmt(value: Expr) extends Stmt

  /**
   * Loop statement.
   */
  case class LoopStmt(cond: Expr, stmt: Stmt, update: Option[Expr]) extends Stmt

  /**
   * Analyzed "when" statement.
   */
  case class AnalyzedWhenStmt(whenVar: List[WhenVar], cond: Expr, stmt:Stmt) extends Stmt
  /**
   * Statement list.
   */
  case class StmtList(stmts: List[Stmt]) extends Stmt

  /**
   * Variable initializer.
   */
  case class VarInit(override val name: String, override val id: Int, value: Expr) extends Stmt with VarInfo

  /**
   * Table initializer.
   */
  case class TableInit(override val id: Int, 
                       override val name: String, 
                       aggr: String, 
                       args: List[Expr], 
                       dataType: TableType, 
                       serializationInfo: TableSerializationInfo) extends Stmt with VarInfo

  /**
   * Static variable definition statement.
   */
  case class DefStaticVarStmt(override val id: Int, 
                              override val name: String, 
                              value: Expr) extends Stmt with VarInfo

  /**
   * Function argument.
   */
  sealed abstract class Arg(val dataType: DataType) extends Positional

  /**
   * Unused argument.
   */
  case class UnusedArg(dt: DataType) extends Arg(dt)

  /**
   * Used argument.
   */
  case class UsedArg(override val name: String, override val id: Int, dt: DataType) extends Arg(dt) with VarInfo

  // Scope
  /**
   * Makes "if" statement and sets scope.
   */
  def mkIfStmt(cond: Expr, thenStmt:Stmt, elseStmt:Option[Stmt]) = IfStmt(cond, Scope(thenStmt), elseStmt.map(Scope))

  /**
   * Makes block statement.
   * { statement... }
   */
  def mkBlockStmt(stmts: List[Stmt]) = Scope(StmtList(stmts)) 
  
  /**
   * Makes "while" statement and sets scope.
   */
  def mkWhileStmt(cond: Expr, stmt: Stmt)  = LoopStmt(cond, Scope(stmt), None)

  /**
   * Makes "for" statement and sets scope.
   */
  def mkForStmt(init: List[Stmt], cond: Option[Expr], update: Option[Expr], stmt: Stmt) =
      Scope(StmtList(init ++ List(LoopStmt(cond.getOrElse(IntLit(1)), Scope(stmt), update))))

  /**
   * Makes "when" statement and sets scope.
   */
  def mkWhenStmt(vars: List[WhenDefVar], cond:Expr, stmt:Stmt)   = Scope(WhenStmt(vars, cond, Scope(stmt)))

  /**
   * Identifier.
   *
   * Any string(with Positional).
   */
  case class Identifier(name: String) extends Positional

  /**
   * Label(name: dataType or dataType)
   */
  case class Label(name: String, dataType: DataType) extends Positional

  /**
   * Table option.
   */
  case class TableOption(id: String, label: Label) extends Positional {
    val dataType = label.dataType
  }

  /**
   * Emit option.
   */
  case class EmitOption(id: String, value: Expr) extends Node with Positional

  /**
   * "when" variable modifier.
   */
  sealed trait WhenMod extends Positional

  /**
   * "some" modifier
   */
  case class SomeMod() extends WhenMod

  /**
   * "each" modifier
   */
  case class EachMod() extends WhenMod

  /**
   * "all" modifier
   */
  case class AllMod()  extends WhenMod

  /**
   * "when" variable definition(analyzed).
   */
  case class WhenVar(override val name: String, override val id: Int, container: Expr, whenType: WhenMod) extends Node with VarInfo {
    def isSome() = whenType.isInstanceOf[SomeMod]
    def isEach() = whenType.isInstanceOf[EachMod]
    def isAll()  = whenType.isInstanceOf[AllMod]
  }

  /**
   * "when" variable definition(not analyzed).
   */
  case class WhenDefVar(names: List[Identifier], mod: WhenMod, dataType: DataType) extends Positional
}
